package com.cuixichang.minimalism.acl;

import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cuixichang.minimalism.basal.enums.DefaultNumberEnum;
import com.cuixichang.minimalism.core.mybatis.cnstants.BaseFieldNames;
import com.cuixichang.minimalism.core.security.userdetails.CredentialDetail;
import com.cuixichang.minimalism.mapper.ILoginMapper;
import com.cuixichang.minimalism.service.*;
import com.cuixichang.minimalism.transfer.dto.*;
import com.cuixichang.minimalism.transfer.po.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class RelevanceDesignImpl implements RelevanceDesign{
    @Autowired
    public ILoginMapper iLoginMapper;
    @Autowired
    private ILoginRoleService iLoginRoleService;
    @Autowired
    private IRoleService iRoleService;
    @Autowired
    private IPermissionService iPermissionService;
    @Autowired
    private IRolePermissionService rolePermissionService;
    @Autowired
    private IRoleMenuService roleMenuService;
    @Autowired
    private IMenuService iMenuService;
    @Autowired
    private IRouterService routerService;
    @Autowired
    private IRouterRelationService routerRelationService;
    @Override
    public List<Long> beAuthLoginIds(Long loginId) {
        List<ILogin> iLogins = iLoginMapper.selectList(new QueryWrapper<ILogin>().select("id")
                .eq("create_login_id", loginId));
        List<Long> result = new ArrayList<>();
        result.add(loginId);
        if(!CollectionUtils.isEmpty(iLogins)){
            List<Long> one = iLogins.stream().map(ILogin::getId).collect(Collectors.toList());
            result.addAll(one);
            for (Long id:one){
                List<Long> two = beAuthLoginIds(id);
                if(!CollectionUtils.isEmpty(two)){
                    result.addAll(two);
                }
            }
        }
        return result.stream().distinct().collect(Collectors.toList());
    }

    @Override
    public Map<Long, String> associatedAccountName(List<Long> loginIds) {
        Map<Long,String> result = MapUtil.createMap(HashMap.class);
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication instanceof AnonymousAuthenticationToken){
            result.put(0L, "系统管理员");
        }else {
            result.put(0L, "系统管理员");
            Object principal = authentication.getPrincipal();
            if(Objects.nonNull(principal)){
                CredentialDetail detail = (CredentialDetail)principal;
                result.put(detail.getLoginId(), detail.getUsername());
            }
        }
        if(!CollectionUtils.isEmpty(loginIds)){
            List<Long> storedKeys = result.keySet().stream().collect(Collectors.toList());
            if(!storedKeys.containsAll(loginIds)){
                List<ILogin> iLogins = iLoginMapper.selectBatchIds(loginIds);
                if(!CollectionUtils.isEmpty(iLogins)){
                    iLogins.forEach(login->{
                        result.put(login.getId(),login.getLoginName());
                    });
                }
            }
        }
        return result;
    }

    @Override
    public IRoleDTO checkRole( Long roleId,List<Long> allowCreateLoginIds,CredentialDetail credentialDetail) {
        return Optional.ofNullable(credentialDetail).map(credential -> {
            Long loginId = credential.getLoginId();
            List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().loginId(loginId).build());
            return iRoleService.selectList(IRoleDTO.builder().build(), (iRole, queryWrapper) -> {
                queryWrapper.eq(BaseFieldNames.ID, roleId);
                queryWrapper.and(wrapper -> {
                    wrapper.in(BaseFieldNames.CREATE_LOGIN_ID, allowCreateLoginIds);
                    wrapper.or().in(!CollectionUtils.isEmpty(iLoginRoles), BaseFieldNames.ID, iLoginRoles.stream()
                            .map(ILoginRoleDTO::getRoleId).distinct().collect(Collectors.toList()));
                });
            }).stream().findFirst().orElse(null);
        }).orElseGet(() -> iRoleService.selectById(roleId));
    }

    @Override
    public IPermissionDTO checkPermission(Long permissionId, List<Long> allowCreateLoginIds, CredentialDetail credentialDetail) {
         return Optional.ofNullable(credentialDetail).map(credential ->
                iPermissionService.selectList(IPermissionDTO.builder().build(), (iMenu, queryWrapper) -> {
                    queryWrapper.eq(BaseFieldNames.ID, permissionId);
                    queryWrapper.and(wrapper -> {
                        wrapper.in(BaseFieldNames.CREATE_LOGIN_ID, allowCreateLoginIds);
                    });
         }).stream().findFirst().orElse(null)).orElseGet(() -> iPermissionService.selectById(permissionId));
    }

    @Override
    public boolean rolePermissionBindingStatus(Long roleId, Long permissionId, boolean toBinding, CredentialDetail credentialDetail) {
        Assert.notNull(roleId,"角色Id不能为空,请检查");
        Assert.notNull(permissionId,"权限Id不能为空,请检查");
        boolean invoke = false;
        List beAuthLoginIds = Optional.ofNullable(credentialDetail).map(credential -> beAuthLoginIds(credential.getLoginId()))
                .orElse(Collections.EMPTY_LIST);
        if(Objects.isNull(checkRole(roleId, beAuthLoginIds, credentialDetail))) return invoke;
        if(Objects.isNull(checkPermission(permissionId, beAuthLoginIds, credentialDetail))) return invoke;
        IRolePermissionDTO build = IRolePermissionDTO.builder().roleId(roleId).permissionId(permissionId).build();
        if(toBinding) {
            build.setCreateLoginId(Optional.ofNullable(credentialDetail).map(CredentialDetail::getLoginId).orElse(0L));
            IRolePermissionDTO rolePermission = rolePermissionService.insertHasOption(build, (dto)-> rolePermissionService.selectList(
                    IRolePermissionDTO.builder().roleId(roleId).permissionId(permissionId).build()
            ).stream().findFirst().orElse(null));
            invoke = Objects.nonNull(rolePermission);
        }else {
            invoke = rolePermissionService.deleteByCondition(build) > DefaultNumberEnum.Zero.getCode();
        }
        return invoke;
    }

    @Override
    public boolean roleMenuBindingStatus(Long roleId, Long menuId, boolean bindingStatus, CredentialDetail credentialDetail) {
        Assert.notNull(roleId,"角色Id不能为空,请检查");
        Assert.notNull(menuId,"菜单Id不能为空,请检查");
        boolean invoke = false;
        List beAuthLoginIds = Optional.ofNullable(credentialDetail).map(credential -> beAuthLoginIds(credential.getLoginId()))
                .orElse(Collections.EMPTY_LIST);
        if(Objects.isNull(checkRole(roleId, beAuthLoginIds, credentialDetail))) return invoke;
        IMenuDTO menu = Optional.ofNullable(credentialDetail).map(credential ->
                iMenuService.selectList(IMenuDTO.builder().build(), (iMenu, queryWrapper) -> {
                    queryWrapper.eq(BaseFieldNames.ID, menuId);
                    queryWrapper.and(wrapper -> {
                        wrapper.in(BaseFieldNames.CREATE_LOGIN_ID, beAuthLoginIds);
                    });
                }).stream().findFirst().orElse(null)).orElseGet(() -> iMenuService.selectById(menuId));
        if(Objects.isNull(menu)) return invoke;
        IRoleMenuDTO build = IRoleMenuDTO.builder().roleId(roleId).menuId(menuId).build();
        if(bindingStatus) {
            build.setCreateLoginId(Optional.ofNullable(credentialDetail).map(CredentialDetail::getLoginId).orElse(0L));
            IRoleMenuDTO roleMenu = roleMenuService.insertHasOption(build, (dto)-> roleMenuService.selectList(
                    IRoleMenuDTO.builder().roleId(roleId).menuId(menuId).build()
            ).stream().findFirst().orElse(null));
            invoke = Objects.nonNull(roleMenu);
        }else {
            invoke = roleMenuService.deleteByCondition(build) > DefaultNumberEnum.Zero.getCode();
        }
        return invoke;
    }

    @Override
    public boolean roleLoginBindingStatus(Long roleId, Long loginId, boolean bindingStatus, CredentialDetail credentialDetail) {
        Assert.notNull(roleId,"角色Id不能为空,请检查");
        Assert.notNull(loginId,"账号Id不能为空,请检查");
        boolean invoke = false;
        List beAuthLoginIds = Optional.ofNullable(credentialDetail).map(credential -> beAuthLoginIds(credential.getLoginId()))
                .orElse(Collections.EMPTY_LIST);
        if(!beAuthLoginIds.contains(loginId)) return invoke;
        if(Objects.isNull(checkRole(roleId, beAuthLoginIds, credentialDetail))) return invoke;
        ILoginRoleDTO build = ILoginRoleDTO.builder().roleId(roleId).loginId(loginId).build();
        if(bindingStatus) {
            build.setCreateLoginId(Optional.ofNullable(credentialDetail).map(CredentialDetail::getLoginId).orElse(0L));
            ILoginRoleDTO loginRole = iLoginRoleService.insertHasOption(build, (dto)-> iLoginRoleService.selectList(
                    ILoginRoleDTO.builder().roleId(roleId).loginId(loginId).build()
            ).stream().findFirst().orElse(null));
            invoke = Objects.nonNull(loginRole);
        }else {
            invoke = iLoginRoleService.deleteByCondition(build) > DefaultNumberEnum.Zero.getCode();
        }
        return invoke;
    }

    @Override
    public boolean routerBindingStatus(Long routerId, Long relationId, boolean isRole, boolean bindingStatus, CredentialDetail credentialDetail) {
        Assert.notNull(routerId,"路由Id不能为空,请检查");
        Assert.notNull(relationId,(isRole?"角色":"权限")+"Id不能为空,请检查");
        boolean invoke = false;
        List beAuthLoginIds = Optional.ofNullable(credentialDetail).map(credential -> beAuthLoginIds(credential.getLoginId())).orElse(Collections.EMPTY_LIST);
        if(Objects.isNull(isRole?
                    checkRole(relationId, beAuthLoginIds, credentialDetail) :
                    checkPermission(relationId, beAuthLoginIds, credentialDetail))) return invoke;
        IRouterDTO router = Optional.ofNullable(routerService.selectById(routerId)).filter(r ->
                CollectionUtils.isEmpty(beAuthLoginIds) || beAuthLoginIds.contains(r.getCreateLoginId())).orElse(null);
        if(Objects.isNull(router)) return invoke;

        IRouterRelationDTO routerRelationDTO = IRouterRelationDTO.builder().routerId(routerId).relationId(relationId).
                roleRelation(isRole).build();
        if(bindingStatus) {
            routerRelationDTO.setCreateLoginId(Optional.ofNullable(credentialDetail).map(CredentialDetail::getLoginId).orElse(0L));
            IRouterRelationDTO iRouterRelation = routerRelationService.insertHasOption(routerRelationDTO, (dto) -> {
                IRouterRelationDTO first = routerRelationService.selectList(IRouterRelationDTO.builder().routerId(routerId)
                        .relationId(relationId).roleRelation(isRole).build()).stream().findFirst().orElse(null);
                return first;
            });
            invoke = Objects.nonNull(iRouterRelation);
        }else {
            invoke = routerRelationService.deleteByCondition(routerRelationDTO) > DefaultNumberEnum.Zero.getCode();
        }
        return invoke;
    }
}
